#include "stdafx.h"
#include "XConfigNodeImpl.h"

#include "XStrUtil.h"
#include "Util.h"
#include "XEnumConfigNodeImpl.h"
#include "XEnumConfigAttrImpl.h"
#include "FileUtil.h"

IMPLEMENT_SKIN_CLASS(CXConfigNodeImpl);

CXConfigNodeImpl::CXConfigNodeImpl()
{
    m_pNode = NULL;
}

CXConfigNodeImpl::~CXConfigNodeImpl()
{
    m_pNode = NULL;
}

// IXConfigNode
HRESULT CXConfigNodeImpl::EnumNode(IN LPCTSTR szNodePath, IN LPCTSTR szNodeName, OUT IXEnum** ppEnum)
{
    TiXmlElement* pParentNode = GetElementByPath(szNodePath);
    if(pParentNode == NULL)
        return HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);

    TiXmlElement* pNode = NULL;
    if(szNodeName == NULL || szNodeName[0] == 0)
    {
        pNode = pParentNode->FirstChildElement();
    }
    else
    {
        std::string strNodeName = XStrUtil::StrToAnsi(szNodeName);
        pNode = pParentNode->FirstChildElement(strNodeName.c_str());
    }
    if(pNode == NULL)
        return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);

    HRESULT hResult = E_FAIL;
    IXEnumConfigNode* pEnumObj = NULL;
    if((*ppEnum) == NULL)
    {
        hResult = Util::CreateObject(&pEnumObj);
        if(FAILED(hResult))
            return hResult;
        hResult = pEnumObj->put_FirstElement((XHANDLE)pNode, szNodeName);
        if(SUCCEEDED(hResult))
        {
            hResult = pEnumObj->QueryInterface(XINTFID(IXEnum), (void**)ppEnum);
            XSAFE_RELEASE(pEnumObj);
        }
        return hResult;
    }
    else
    {
        hResult = (*ppEnum)->QueryInterface(XINTFID(IXEnumConfigNode), (void**)&pEnumObj);
        if(FAILED(hResult) || pEnumObj == NULL)
            return E_FAIL;

        hResult = pEnumObj->put_FirstElement((XHANDLE)pNode, szNodeName);
        XSAFE_RELEASE(pEnumObj);
        return hResult;
    }
}

HRESULT CXConfigNodeImpl::EnumAttr(OUT IXEnum** ppEnum)
{
    HRESULT hResult = E_FAIL;
    if((*ppEnum) == NULL)
    {
        IXEnumConfigAttr* pEnumObj = NULL;
        hResult = Util::CreateObject(&pEnumObj);
        if(FAILED(hResult))
            return hResult;
        hResult = pEnumObj->QueryInterface(XINTFID(IXEnum), (void**)ppEnum);
        XSAFE_RELEASE(pEnumObj);
        CHECK_HRESULT(hResult);
    }

    CXEnumConfigAttrImpl* pEnum = dynamic_cast<CXEnumConfigAttrImpl*>(*ppEnum);
    if(pEnum == NULL)
    {
        return E_FAIL;
    }

    pEnum->put_Node((XHANDLE)m_pNode);
    return S_OK;
}

void CXConfigNodeImpl::GetNodeName(OUT BSTR* pbstrNodeName)
{
    XString strResult;
    if(m_pNode != NULL)
    {
        if(m_pNode->Type() == TiXmlNode::TINYXML_ELEMENT)
        {
            const char* pName = m_pNode->Value();
            if(pName != NULL)
            {
                strResult = XStrUtil::Utf8ToStr(pName);
            }
        }
    }
    ::SysReAllocString(pbstrNodeName, strResult.GetData());
}

HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT BSTR* pbstrValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue) && ::SysReAllocString(pbstrValue, strValue.GetData()))
        return S_OK;
    else
        return E_FAIL;
}

HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT LONG* plValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue))
    {
        *plValue = XStrUtil::StringToLong(strValue.GetData());
        return S_OK;
    }
    else
    {
        return E_FAIL;
    }
}
HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT BOOL* pbValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue))
    {
        *pbValue = XStrUtil::StringToBool(strValue.GetData());
        return S_OK;
    }
    else
    {
        return E_FAIL;
    }
}
HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT RECT* prcValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue) && XStrUtil::StringToRect(strValue.GetData(), prcValue))
        return S_OK;
    else
        return E_FAIL;
}

HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT SIZE* pSizeValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue) && XStrUtil::StringToSize(strValue.GetData(), pSizeValue))
        return S_OK;
    else
        return E_FAIL;
}

HRESULT CXConfigNodeImpl::GetValue(IN LPCTSTR szValueName, OUT COLORREF* pClrValue)
{
    XString strValue;
    if(GetValueImpl(szValueName, strValue) && XStrUtil::StringToColor(strValue.GetData(), pClrValue))
        return S_OK;
    else
        return E_FAIL;
}

HRESULT CXConfigNodeImpl::SetValue(IN LPCTSTR szValueName, IN LPCTSTR szValue, IN BOOL bSaveFile)
{
    return SetValueImpl(szValueName, szValue, bSaveFile) ? S_OK : E_FAIL;
}

HRESULT CXConfigNodeImpl::DelValue(IN LPCTSTR szValueName, IN BOOL bSaveFile)
{
    HRESULT hResult = S_OK;

    std::string strValueName = XStrUtil::StrToAnsi(szValueName);
    m_pNode->RemoveAttribute(strValueName.c_str());

    if(bSaveFile)
        hResult = SaveFile(NULL);
    return hResult;
}

HRESULT CXConfigNodeImpl::SaveFile(IN LPCTSTR szFilePath)
{
    TiXmlDocument* pDoc = m_pNode->GetDocument();
    if(pDoc == NULL)
    {
        XASSERT_ERROR(FALSE, _T("IXConfigNode Save Failed."));
        return E_FAIL;
    }

    XString strFilePath;
    if(szFilePath == NULL)
    {
        XASSERT_ERROR(pDoc != NULL && pDoc->Value() != NULL && pDoc->Value()[0] != 0, _T("Bad Config"));
        if(pDoc != NULL)
        {
            strFilePath = XStrUtil::AnsiToStr(pDoc->Value());
        }
    }
    else
    {
        strFilePath = szFilePath;
    }
    if(strFilePath.empty())
    {
        XASSERT_ERROR(FALSE, _T("IXConfigNode Save Failed."));
        return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }

    BOOL bResult = FALSE;
    XString strTempPath = FileUtil::GetTempFilePath(NULL);
    if(!strTempPath.empty())
    {
        bResult = pDoc->SaveFile(XStrUtil::StrToAnsi(strTempPath.c_str()).c_str());
        if(bResult)
            bResult = ::MoveFileEx(strTempPath.c_str(), strFilePath.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
    }
    else
    {
        bResult = pDoc->SaveFile();
    }

    XASSERT_ERROR(bResult, _T("IXConfigNode Save Failed."));
    return bResult ? S_OK : E_FAIL;
}

// CXConfigNodeImpl
HRESULT CXConfigNodeImpl::put_Node(IN XHANDLE hNode)
{
    m_pNode = (TiXmlElement*)hNode;
    return (m_pNode != NULL) ? E_FAIL : S_OK;
}

BOOL CXConfigNodeImpl::GetValueImpl(LPCTSTR szValueName, XString& strValue)
{
    if(m_pNode == NULL)
        return FALSE;

    std::string strValueName = XStrUtil::StrToAnsi(szValueName);
    const char* pValue = m_pNode->Attribute(strValueName.c_str());
    if(pValue == NULL)
        return FALSE;

    strValue = XStrUtil::Utf8ToStr(pValue);
    return TRUE;
}

TiXmlElement* CXConfigNodeImpl::GetElementByPath(LPCTSTR szPath)
{
    XString strPath(szPath);
    XStrVector vctNodeNames;
    if(XStrUtil::SplitString(strPath, _T("\\"), vctNodeNames) == 0)
    {
        return m_pNode;
    }
    else
    {
        std::string strNodeName;
        TiXmlElement* pResult = m_pNode;
        XStrVector::iterator IteNodeName = vctNodeNames.begin();
        for(; pResult != NULL && IteNodeName != vctNodeNames.end(); ++ IteNodeName)
        {
            strNodeName = XStrUtil::StrToAnsi(*IteNodeName);
            pResult = pResult->FirstChildElement(strNodeName.c_str());
        }
        return pResult;
    }
}

BOOL CXConfigNodeImpl::SetValueImpl(LPCTSTR szValueName, LPCTSTR szValue, IN BOOL bSaveFile)
{
    if(m_pNode == NULL)
        return FALSE;

    std::string strValueName = XStrUtil::StrToAnsi(szValueName);
    std::string strValue = XStrUtil::StrToUtf8(szValue);
    m_pNode->SetAttribute(strValueName.c_str(), strValue.c_str());

    HRESULT hResult = S_OK;
    XASSERT_ERROR(!bSaveFile || m_pNode->GetDocument() != NULL, _T("CXConfigNodeImpl::SetValueImpl Failed"));
    if(bSaveFile)
    {
        hResult = SaveFile(NULL);
    }
    return SUCCEEDED(hResult);
}
